/** ChartMap 的Id */
let _id = 0;
/**
 * 用来记录 echarts 数据之间的关系
 * @class ChartMap
 * @constructor 初始化数据
 * @param {Array} data  储存原数据列副本
 * @param {Object} mapData  映射的数据
 * @param {String} name  名称
 * @param {Number} _id  Id
 * @method create 创建 mapData 对象
 */
class ChartMap {

  constructor(data, name = "Chart") {
    /** 储存原数据列副本 */
    this.data = JSON.parse(JSON.stringify(data));
    /** 映射的数据 */
    this.mapData = {};
    /** ChartMap 名称 */
    this.name = name == "Chart" ? name + _id : name;
    /** ChartMap Id */
    this._id = _id;
    _id += 1;
  }
  /**
   * 创建映射对象并存入 mapData 中
   * @param {Number|String} x 处于第几行，x 点坐标位置
   * @param {Number|String} y 处于第几列，y 点坐标位置
   * @returns 
   */
  create(x, y) {
    if (this.mapData[`${x},${y}`]) return;
    this.mapData[`${x},${y}`] = {
      /** 原始数据列 */
      originList: this.data[x].data,
      /** 原始数据 */
      originData: Number(this.data[x].data[y]),
      /** 需要与当前点进行位置计算的点集合 */
      compareMap: {},
      /** 距离当前点，绝对位置最近的点对象 */
      NearestPoint:null,
      /** 距离当前点，最近的绝对距离 */
      NearestDistance:0,
      /** 当前点 x 方向上需要位移的距离 */
      offSetx: 0,
      /** 当前点 y 方向上需要位移的距离 */
      offsetY: 0,
      /** 当前点在同组数据中的位置 */
      sort:0,
      /** 当前点的 x 坐标 */
      x,
      /** 当前点的 y 坐标 */
      y,
    };
  }
}

/**
 * 序列化 echarts 数据列 ,修正 echarts label 遮挡问题
 * @param {String} echartType 图表种类：line 线 bar 柱状 （必填）
 * @param {Array} seriesArr echarts 数据列 （必填）
 * @param {Object} labelOption label额外配置项 （选填）
 * @param {Number} AxisMax  坐标系量程 （选填）
 * @param {Number} judgeCoefficient 判断系数，计算触发遮挡的条件 （选填）
 * @param {Number} fixCoefficient 修正系数，遮挡后修正距离的系数 （选填）
 */
 function fixEchartsLabel(
  echartType,
  seriesArr,
  labelOption,
  AxisMax,
  judgeCoefficient = 2.5,
  fixCoefficient = 0.6
) {
  if (Object.prototype.toString.call(seriesArr) !== "[object Array]")
    return new Error("fixEchartsLabel 函数需要传入一个 echarts 数据列数据");
  if (seriesArr.length <= 1) return seriesArr;
 
  // 多少组数据 x
  const dataRowLength = seriesArr.length;
  // 每组数据有多少个(取最多的) y
  let lengerColLength = 0;
  for (let i = 0; i < seriesArr.length; i++) {
    if (Object.prototype.toString.call(seriesArr[i].data) !== "[object Array]")
      continue;
    if (seriesArr[i].data.length > lengerColLength) {
      lengerColLength = seriesArr[i].data.length;
    }
  }
  // 初始化坐标
  let x = 0;
  let y = 0;
  // 存映射点用于最后计算赋值
  const chartMap = new ChartMap(seriesArr);
  // 获取最大值
  let maxValue = 0;
  // 同列数据建立映射关系
  while (x <= dataRowLength && y < lengerColLength) {
    if (x + 1 > dataRowLength) {
      y += 1;
      x = 0;
      continue;
    }
    // 存当前点
    chartMap.create(x, y);
    const cur = chartMap.mapData[`${x},${y}`];
    maxValue = cur.originData > maxValue ? cur.originData : maxValue;
    // 存同组的零时数据，用于非 bar 图对相同值增加 sort
    const temp = [];
    // 最近距离
    let NearestDistance = Infinity;
    // 当前点建立与同列数据的关系
    for (let i = 0; i < dataRowLength; i++) {
      if (i == x) continue;
      // 存比较点，不用担心重复 create 做了重复的判断
      chartMap.create(i, y);
      const target = chartMap.mapData[`${i},${y}`];
      // 这里把同列的点都加入了比较列表中
      cur.compareMap[`${i},${y}`] = target;
      // 确定当前位置排序，非柱状图排除相同值的点
      if(cur.originData > target.originData &&  temp.indexOf(target.originData) == -1){
        cur.sort += 1;
        temp.push(target.originData)
      } else if (echartType == 'bar' && cur.originData == target.originData && target.x > cur.x ){
        target.sort += 1;
      }
      // 绑定最近距离的点
      const distance = Math.abs(target.originData - cur.originData);
      if( distance < NearestDistance ){
        cur.NearestPoint = target;
        cur.NearestDistance = distance;
        NearestDistance = distance;
      }
    }
    x += 1;
  }
  console.log(maxValue,chartMap)
  // AxisMax 用来计算偏移的基础位移
  AxisMax = AxisMax ? AxisMax : maxValue;
  // 返回计算后的数据列
  return calcLableOffset(
    chartMap.data,
    chartMap.mapData,
    labelOption,
    judgeCoefficient,
    fixCoefficient,
    AxisMax
  );
}
/**
 * 计算 echarts label 位移
 * @param {Array} seriesArr echarts 数据列
 * @param {Object} chartMap echarts数据map
 * @param {Object} labelOption label额外配置项
 * @param {Number} judgeCoefficient 判断系数，计算触发遮挡的条件 
 * @param {Number} fixCoefficient 修正系数，遮挡后修正距离的系数 
 * @return {Object} result 计算后的 echarts 数据列
 */
 function calcLableOffset(
  seriesArr,
  chartMap,
  labelOption,
  judgeCoefficient,
  fixCoefficient,
  AxisMax
) {
  // 计算触发数值。
  const judgeDistance = +((+AxisMax * judgeCoefficient) / 100).toFixed(2);
  // 计算修正值。
  const fixDistance = +((+AxisMax * fixCoefficient) / 100).toFixed(2);
  // 循环数据 map 判断是否遮挡、是否位移以及位移距离
  for (let cPoint in chartMap) {
    const cur = chartMap[cPoint];
    //这里将 5 倍临界值作为安全距离， 大于 5 倍临界值就不进行位移
    const offsetY = cur.NearestDistance > 5 * judgeDistance ? 0 : -fixDistance * cur.sort;
    cur.offsetY = offsetY;
    // 重新构建数据列
    seriesArr[cur.x].data[cur.y] = {
      value: cur.originData,
      label: {
        ...labelOption,
        position: 'top',
        offset: [0, offsetY],
        show: true,
      },
    };
  }
  return seriesArr;
}
